package com.way.blockchain.pow;

import com.way.blockchain.block.Block;
import com.way.blockchain.util.ByteUtils;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.commons.codec.cli.Digest;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;

import java.math.BigInteger;

/**
 * @author way
 * @create 2023-09-06 9:30
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ProofOfWork {
    /**
     * 难度目标值
     * 256hash前面至少多少个0，比特币中是根据难度值设置的
     */
    public static final int TARGET_BITS = 16;
    /**
     * 要验证的区块
     */
    private Block block;
    /**
     * 难度目标值
     */
    private BigInteger target;

    /**
     * 创建工作量证明对象
     *
     * @param block 验证的区块
     * @return 工作量证明对象
     */
    public static ProofOfWork newProofOfWork(Block block) {
        //BigInteger.ONE 是一个表示数字 1 的 BigInteger 对象。
        //shiftLeft(n)是BigInteger类的方法，它将当前 BigInteger 对象的二进制表示向左移动n位
        BigInteger targetValue = BigInteger.ONE.shiftLeft(256 - TARGET_BITS);
        return new ProofOfWork(block, targetValue);
    }

    public PowResult run() {
        long nonce = 0;
        String shaHex;
        System.out.println("开始挖矿-------------------");
        long startTime = System.currentTimeMillis();
        while (true) {
            byte[] data = this.prepareData(nonce);
            shaHex = DigestUtils.sha256Hex(data);
            System.out.printf("\r%d: %s",nonce,shaHex);
            if (new BigInteger(shaHex, 16).compareTo(this.target) < 0) {
                System.out.println();
                System.out.printf("耗时 Time: %s seconds \n", (float) (System.currentTimeMillis() - startTime) / 1000);
                System.out.printf("当前区块Hash: %s \n\n", shaHex);
                break;
            } else {
                nonce++;
            }
        }
        return new PowResult(nonce, shaHex);
    }

    /**
     * 准备要挖矿的数据，生成byte[]
     *
     * @param nonce 随机数，这里从1开始
     * @return 拼接后的byte数组
     */
    private byte[] prepareData(long nonce) {
        //空字节数组
        byte[] prevBlockHashBytes = {};
        if (StringUtils.isNoneBlank(this.getBlock().getPrevBlockHash())) {
            //将前一个区块的哈希值解释为一个十六进制字符串
            prevBlockHashBytes = new BigInteger(this.getBlock().getPrevBlockHash(), 16).toByteArray();
        }
        return ByteUtils.merge(
                prevBlockHashBytes,
                this.getBlock().hashTransaction(),
                ByteUtils.toBytes(this.getBlock().getTimeStamp()),
                ByteUtils.toBytes(TARGET_BITS),
                ByteUtils.toBytes(nonce));
    }
    /**
     * 验证区块是否有效
     * @return
     */
    public boolean validate() {
        byte[] data = this.prepareData(this.getBlock().getNonce());
        return new BigInteger(DigestUtils.sha256Hex(data), 16).compareTo(this.target) < 0;
    }
}
